from django.test import TestCase

# Create your tests here.
from rest_framework.pagination import PageNumberPagination
from collections import OrderedDict
from rest_framework.response import Response
from rest_framework.exceptions import ValidationError
from base_utils.exception import ParamsError
from base_utils.response_http_code import SuccessStatus


class BasePagination(PageNumberPagination):
    page_size = 10  # 默认的每页大小
    page_query_param = "page"  # 查询页码的字符串
    page_size_query_param = "page_size"  # 查询每页大小的字符串
    max_page_size = page_size * 2  # 限制每页最大数据量
    # 给响应器判断是否是分页数据，做处理
    is_page_data = "PAGE-DATA"

    def __init__(self):
        self.total_count = None  # 总数据量
        self.total_page = None  # 总页数
        self.current_page = None  # 当前页码
        self.has_next_page = None  # 是否有下一页
        self.has_previous_page = None  # 是否有上一页

    # 重写父类的: 校验page_size
    def get_page_size(self, request):
        """
        功能：校验page_size，没有传递或传递类型有问题时，按照默认的大小设置，超过最大页面大小时，设置成最大页面
        :param request:
        :return: 数值
        """
        page_size = request.GET.get(self.page_size_query_param)
        if not page_size:
            page_size = self.page_size  # 取的是类的page_size
        else:
            try:
                page_size = int(page_size)
            except Exception:
                page_size = self.page_size  # 取的是类的page_size
        if page_size > self.max_page_size:
            page_size = self.max_page_size
        # 给分页的实例对象赋值每页大小
        self.page_size = page_size
        return self.page_size

    # 自己定义: 校验page
    def get_page(self, request, queryset, page_size):
        """
        功能：判断携带page是否合法，页码必须大于0，不能大于最大页数，页码必须是数值
        :param request: 当前请求对象
        :param queryset: 模型uqeryset
        :param page_size:  每页大小
        :return: None，{"error":'错误消息'}，数值
        自定义的
        """
        page = request.GET.get(self.page_query_param)
        if page == None:
            raise ParamsError(f"必须携带{self.page_query_param}此参数")

        try:
            page = int(page)
        except ValueError:
            raise ParamsError(f"{self.page_query_param}页码必须是数值")

        # 1、判断查询的页码是否大于0
        if page <= 0:
            raise ParamsError(f"{self.page_query_param}页码必须是大于0")
        # 2、总数据量
        self.total_count = len(queryset)
        # 3、判断查询的数据是否有数据
        if self.total_count == 0:
            raise ParamsError(f"查询不到相关数据")
        # 3、总页数
        self.total_page, has_number = divmod(self.total_count, page_size)
        if has_number:
            self.total_page += 1

        # 4、判断查询的页码是否大于总页码
        if page > self.total_page:
            self.current_page = self.total_page
        else:
            # 5、当前的页码
            self.current_page = page
        return self.current_page

    # 重写父类的: 获取当前页的QuerySet对象
    def paginate_queryset(self, queryset, request, view=None):
        """
        功能：获取当前页的queryset对象
        :param queryset: 所有的queryset
        :param request: 当前请求
        :param view: 当前视图类
        :param serializer_class: 手动添加时的start方法会调用这个，ListModelMixin不会传递这个的
        :return: None，{’error‘:''}, QuerySet
        """
        page_size = self.get_page_size(request)
        page = self.get_page(request, queryset, page_size)

        ##1、实例化django的分页器
        paginator = self.django_paginator_class(queryset, page_size)

        ##2、获取当前页数据: 使用django的分页器来实现的
        self.page = paginator.page(self.current_page)
        self.request = request

        ##3、是否有上一页和下一页
        self.has_next_page = 1 if self.get_next_link() else 0
        self.has_previous_page = 1 if self.get_previous_link() else 0

        return list(self.page)

    # 重写父类的：将当前页的QuerySet对象序列化
    def get_paginated_response(self, page_queryset, serializer_class=None):
        """
        功能：根据传递进来的dict，None（page没有传递，就不返回数据了），当前页的List，当前页QuerySet，
        :param page_queryset: dict,None,List，QuerySet
        :param serializer_class: 序列化器类
        :return: {}
        """
        if isinstance(page_queryset, list):
            data = {
                "code": SuccessStatus.http_code,
                "msg": "获取数据成功",
                "data": "",  # 当前页数据
                "next": self.has_next_page,  # 是否有下一页
                "previous": self.has_previous_page,  # 是否有上一页
                "count": self.total_count,  # 总数据量
                "pages": self.total_page,  # 总页码数
                "current_page": self.current_page,  # 当前页码
                "page_size": self.page_size,  # 每页大小
                f"{self.is_page_data}": 1,  # 让响应类特殊处理分页数据
            }
            if serializer_class:
                # 1、手动使用start方法调用时,
                page_ser = serializer_class(instance=page_queryset, many=True)
                page_data = page_ser.data
                data["data"] = page_data
                return data
            else:
                # 2、配合mixins.ListModelMixin源码使用, 其调用了paginate_queryset，拿到queryset对象，将序列化结果传递进来
                page_queryset = self.handle_data(page_queryset)
                data["data"] = page_queryset
                return Response(OrderedDict([(k, v) for k, v in data.items()]))
        else:
            raise Exception("分页器只支持，dict，list，QuerySet，None 类型")

    # 自己定义的，对当前页数据，进一步的处理
    def handle_data(self, data_list):
        return data_list

    def start(self, queryset, request, serializer_class=None):
        # 手动调用时，跟ListModelMixin一样调用过程
        # 1、获取当前页的QuerySet对象
        page_queryset = self.paginate_queryset(queryset=queryset, request=request)
        # 2、将当前页的QuerySet对象传递进去，得到分页的响应结果
        page_data = self.get_paginated_response(
            page_queryset=page_queryset, serializer_class=serializer_class
        )
        return page_data

    def __call__(self, queryset, request, serializer_class=None):
        return self.start(queryset, request, serializer_class)


class ApiPagination(BasePagination):
    pass


if __name__ == "__main__":
    """
    mixins.ListModelMixin的调用逻辑：
        1、调用paginate_queryset
        2、1的返回值如果是None时，就不执行获取分页的数据，就去获取所有的数据返回
        3、1的返回值不为None时，就执行get_paginated_response，拿到分页的结果
    手动调用使用：
        data = page.start(request,序列化类,模型对象)
        data = page(request,序列化类,模型对象)
    """
    page = ApiPagination()
    page_data = page.start("request对象", "序列化器类", "数据库查询的queryset")
